#include "StdAfx.h"
#include "BuilderCSV.h"
#include <iosfwd>
#include <direct.h>

//////////////////////////////////////////////////////////////////////////
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	03/07/2014
//
//This class builds a PPR tree structure from CSV data
//////////////////////////////////////////////////////////////////////////

BuilderCSV::BuilderCSV(void)
{
}

bool BuilderCSV::Is_number(string s)
{
	std::locale loc;
	std::string::const_iterator it = s.begin();
	while (it != s.end() && std::isdigit(*it, loc)) ++it;
	return !s.empty() && it == s.end();
}

//////////////////////////////////////////////////////////////////////////
//Builds PPR branches from input csv rows
//same steps as below but list of string instead of input file
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::BuildFromCSV(hash_map<string, PPR> branches, list<string> inputRows, string actionCol, vector<string> actions, 
												string observationCol, vector<string> observations, string stateCol, int treeLength, 
												vector<string> states, vector<string> joinStates)
{
	list<PPRObservationAction> pairs = ConvertDataToPairs(inputRows, actionCol, actions, observationCol, observations, joinStates);
	branches = PairsToTrees(branches, pairs, observations, treeLength);

	return branches;
}


//////////////////////////////////////////////////////////////////////////
//Builds a list of PPRs from data in csv files
//
//Algorithm
//1.Convert CSV into a list of objects, each containing observation-action
//	pairs
//2.Loop through t time steps of objects to build a branch
//3.Determine if the branch fits any existing PPR trees, if it does add it
//	to them, if not start a new tree using the branch
//4.Continue until all data is exhausted
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::BuildFromCSV(hash_map<string, PPR> branches, string inputFile, string actionCol, vector<string> actions, 
												string observationCol, vector<string> observations, string stateCol, int treeLength, 
												vector<string> states, vector<string> joinStates)
{
	//hash_map<string, PPR> trees;

	ifstream csvFile(inputFile.c_str());

	string temp;
	list<string> rows;

	while(getline(csvFile, temp))
	{
		rows.push_back(temp);
	}

	return BuildFromCSV(branches, rows, actionCol, actions, observationCol, observations, stateCol, treeLength, states, joinStates);
}

//////////////////////////////////////////////////////////////////////////
//Specific to the StarCraft domain, learns from starcraft replay converted
//data, stars a new branch when any of the following conditions is met
//branch length = max tree length
//any of the landmark columns change value
//time to last step is greater than 2 time steps i.e. > 2000
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::BuildFromStarCraftUnitHistory(hash_map<string, PPR> branches, string inputFile, string actionCol, vector<string> actions,
	string observationCol, vector<string> observations, vector<string> joinStates, vector<string> landmarkCols, string timeCol, int timeStepDuration, int maxTreeLength)
{
	//Convert into rows
	ifstream csvFile(inputFile.c_str());
	string temp;
	list<string> rows;
	while(getline(csvFile, temp))
	{
		rows.push_back(temp);
	}

	//Convert into StarCraft rows
	string colHeaders = rows.front();
	rows.pop_front();
	vector<int> landmarkIndexes;
	vector<int> joinStateIndexes;
	int actionIndex;
	int observationIndex;
	int timeIndex;
	int i = 0;
	while (colHeaders.find(",", 0) != string::npos)
	{
		size_t  pos = colHeaders.find(",", 0);
		temp = colHeaders.substr(0, pos);
		colHeaders.erase(0, pos + 1);
		if(temp == actionCol)
		{
			actionIndex = i;
		}
		else if(temp == observationCol)
		{
			observationIndex = i;
		}
		else if (temp == timeCol)
		{
			timeIndex = i;
		}
		else
		{
			for(vector<string>::iterator it = joinStates.begin(); it != joinStates.end(); it++)
			{
				string joinState = *it;
				if(joinState == temp)
				{
					joinStateIndexes.push_back(i);
				}
			}

			for(vector<string>::iterator it = landmarkCols.begin(); it != landmarkCols.end(); it++)
			{
				string landmarkCol = *it;
				if(landmarkCol == temp)
				{
					landmarkIndexes.push_back(i);
				}
			}
		}

		i++;
	}

	list<StarCraftRow> starcraftRows;
	//Parse rows into StarCraftRows
	while(rows.size() > 0)
	{
		string row = rows.front();
		StarCraftRow scRow;

		int i = 0;
		while (row.find(",", 0) != string::npos)
		{
			size_t  pos = row.find(",", 0);
			temp = row.substr(0, pos);
			row.erase(0, pos + 1);

			if(i == actionIndex)
			{
				scRow.action = temp;
			}
			else if(i == observationIndex)
			{
				scRow.observation = temp;
			}else if (i == timeIndex)
			{
				scRow.time = atoi(temp.c_str());
			}
			else
			{
				for(vector<int>::iterator it = joinStateIndexes.begin(); it != joinStateIndexes.end(); it++)
				{
					int ti = *it;
					if(i == ti)
					{
						scRow.joinStates.push_back(temp);
					}
				}

				for(vector<int>::iterator it = landmarkIndexes.begin(); it != landmarkIndexes.end(); it++)
				{
					int ti = *it;
					if(i == ti)
					{
						scRow.landmarks.push_back(temp);
					}
				}
			}
				

			i++;
		}
		rows.pop_front();
		starcraftRows.push_back(scRow);		
	}


	//Build branches using the following rules
	//length = max tree length
	//any landmark value has changed
	//time between steps greater than 2x time step duration
	//Achieve this by converting to list of PPRObservationAction then entering into pairs to trees to get branch, then enter branch and existing branches into MergeBranchInWithTrees
	list<PPRObservationAction> currentBranch;
	int lastTime = -1;
	vector<string> lastLandmarks;
	for(list<StarCraftRow>::iterator it = starcraftRows.begin(); it != starcraftRows.end(); it++)
	{
		StarCraftRow tempScRow = *it;

		//Initial values
		if (lastTime == -1)
		{
			lastTime = tempScRow.time;
		}
		else if((tempScRow.time - lastTime) >= (2 * timeStepDuration))
		{
			branches = PairsToTrees(branches, currentBranch, observations, currentBranch.size());
			currentBranch.clear();
		}
		lastTime = tempScRow.time;


		if(lastLandmarks.size() == 0)
		{
			lastLandmarks = tempScRow.landmarks;
		}
		
		bool landmarkChanged = false;
		for (int i = 0; i < lastLandmarks.size() && !landmarkChanged; i++)
		{
			if(lastLandmarks[i] != tempScRow.landmarks[i])
			{
				landmarkChanged = true;
				branches = PairsToTrees(branches, currentBranch, observations, currentBranch.size());
				currentBranch.clear();
			}
		}
		lastLandmarks = tempScRow.landmarks;

		//Convert ScRow to PPRObservationAction
		//Need to combine states and add to state variable if size = 0
		PPRObservationAction tempPprObsAct;
		tempPprObsAct.action = tempScRow.action;
		tempPprObsAct.observation = tempScRow.observation;
		if (currentBranch.size() == 0) //if first in branch then need to combine join states
		{
			stringstream ss;
			for (vector<string>::iterator itj = tempScRow.joinStates.begin(); itj != tempScRow.joinStates.end(); itj++)
			{
				string jState = *itj;
				ss << jState;
			}
			tempPprObsAct.state = ss.str();
		}
		currentBranch.push_back(tempPprObsAct);
		
		if (currentBranch.size() >= maxTreeLength) // enter list into PairsToTrees
		{
			branches = PairsToTrees(branches, currentBranch, observations, currentBranch.size());
			currentBranch.clear();
		}
	}

	return branches;
}



//////////////////////////////////////////////////////////////////////////
//Converts data with identified histories into branches then uses existing
//algorithms to merge.
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::BuildFromCSVHistories(hash_map<string, PPR> branches, string inputFile, string actionCol, vector<string> actions,
												vector<string> observations, string joinState, string historyCol)
{
	//Get Rows From File
	ifstream csvFile(inputFile.c_str());
	string temp;
	list<string> rows;
	while(getline(csvFile, temp))
	{
		rows.push_back(temp);
	}

	//Build a branch for each row, leaf node is the value in action column and parents are created from the history column
	//First identify column headers
	string colHeaders = rows.front();
	rows.pop_front();
	int actionIndex = 0;
	int historyIndex = 0;
	int joinIndex = 0;
	int i = 0;
	while (colHeaders.find(",", 0) != string::npos)
	{
		size_t  pos = colHeaders.find(",", 0);
		temp = colHeaders.substr(0, pos);
		colHeaders.erase(0, pos + 1);
		if(temp == actionCol)
		{
			actionIndex = i;
		}
		else if(temp == historyCol)
		{
			historyIndex = i;
		}
		else if(temp ==	joinState)
		{
			joinIndex = i;
		}
		i++;
	}

	//loop through each row to extract the action, history and join information
	list<ActHistJoin> parsed = ParseHistoryRows(rows, actionIndex, historyIndex, joinIndex, actions);

	for(list<ActHistJoin>::iterator it = parsed.begin(); it != parsed.end(); it++)
	{
		ActHistJoin tempAHJ = *it;
		PPR branch = HistoryToBranch(tempAHJ, actions, observations);

		cout << "x";

		if(branch.GetRootNode()->GetChildNodes().size() > 0)
		{
			branches = MergeBranchInWithTrees(branches, branch);
		}		
	}

	return branches;
}

PPR BuilderCSV::HistoryToBranch(ActHistJoin history, vector<string> actions, vector<string> observations)
{
	list<string> observationsList;
	copy(observations.begin(), observations.end(), back_inserter(observationsList));

	ActionNode * currentNode = new ActionNode(history.action, observationsList, history.join);

	int i = 0;
	string lastObs;
	for(list<string>::reverse_iterator it = history.history.rbegin(); it != history.history.rend(); it++)
	{
		string temp = *it;

		if(temp != "")
		{
			if(i % 2 == 0)
			{
				lastObs = temp;
			}
			else
			{
				ActionNode * newParent = new ActionNode(temp, observationsList, history.join);
				//currentNode->SetParent(newParent);
				newParent->AddChildNode(lastObs, currentNode);
				currentNode = newParent;
			}
		}

		i++;
	}

	PPR newPPR(currentNode);

	return newPPR;
}


list<ActHistJoin> BuilderCSV::ParseHistoryRows(list<string> rows, int actionIndex, int historyindex, int joinIndex, vector<string> actions)
{
	list<ActHistJoin> parsed;

	for(list<string>::iterator it = rows.begin(); it != rows.end(); it++)
	{
		string row = *it;
		ActHistJoin parsedRow;

		int i = 0;
		while (row.find(",", 0) != string::npos)
		{
			string temp;
			size_t  pos = row.find(",", 0);
			temp = row.substr(0, pos);
			row.erase(0, pos + 1);

			if(i == actionIndex)
			{
				parsedRow.action = temp;
				if(Is_number(temp))
				{
					parsedRow.action = actions[atoi(temp.c_str())];
				}
			}
			else if(i == historyindex)
			{
				//Parse history string separate by - symbol
				while (temp.find("-", 0) != string::npos)
				{
					string tempHist;
					size_t posHist = temp.find("-", 0);
					tempHist = temp.substr(0, posHist);
					temp.erase(0, posHist + 1);
					parsedRow.history.push_back(tempHist);
				}
				parsedRow.history.push_back(temp);
			}
			else if(i == joinIndex)
			{
				parsedRow.join = temp;
			}

			i++;
		}

		parsed.push_back(parsedRow);
	}

	return parsed;
}

//////////////////////////////////////////////////////////////////////////
//Converts csv data from list of strings to pairs same as above method
//but uses list of strings instead of input file
//////////////////////////////////////////////////////////////////////////
list<PPRObservationAction> BuilderCSV::ConvertDataToPairs(list<string> rows, string actionCol, vector<string> actions, string observationCol, 
															vector<string> observations, vector<string> joinStates)
{
	list<PPRObservationAction> pairs;
	string temp;

	if(rows.size() > 0)
	{
		//Get the index of the headings for actions and observations
		string colHeaders = rows.front();
		rows.pop_front();
		int actionIndex = 0;
		int observationindex = 0;	
		
		//int stateIndex = -1;
		list<int> stateIndexes;
		
		int i = 0;
		while (colHeaders.find(",", 0) != string::npos)
		{
			size_t  pos = colHeaders.find(",", 0);
			temp = colHeaders.substr(0, pos);
			colHeaders.erase(0, pos + 1);
			if(temp == actionCol)
			{
				actionIndex = i;
			}
			else if(temp == observationCol)
			{
				observationindex = i;
			}
			else //if(temp == stateCol)
			{
				//stateIndex = i;
				for(vector<string>::iterator it = joinStates.begin(); it != joinStates.end(); it++)
				{
					string joinState = *it;
					if(joinState == temp)
					{
						stateIndexes.push_back(i);
					}
				}				
			}

			i++;
		}

		//Parse each row and select the action and observation from each row
		while(rows.size() > 0)
		{
			string row = rows.front();

			PPRObservationAction pair;

			int i = 0;
			while (row.find(",", 0) != string::npos)
			{
				size_t  pos = row.find(",", 0);
				temp = row.substr(0, pos);
				row.erase(0, pos + 1);

				if(Is_number(temp))
				{
					int index = atoi(temp.c_str());

					if(i == actionIndex)
					{
						pair.action = actions[index];
					}
					else if(i == observationindex)
					{
						pair.observation = observations[index];
					}
					else// if(i == stateIndex)
					{
						/*if(states.size() > 0)
						{
						pair.state = states[index];
						}
						else
						{
						pair.state = temp;
						}*/		

						for(list<int>::iterator it = stateIndexes.begin(); it != stateIndexes.end(); it++)
						{
							int ti = *it;

							if(i == ti)
							{
								stringstream ss;
								//ss << pair.state << states[index];
								ss << pair.state << temp;
								pair.state = ss.str();
							}
						}
					}
				}
				else
				{
					if(i == actionIndex)
					{
						pair.action = temp;
					}
					else if(i == observationindex)
					{
						pair.observation = temp;
					}
					else// if(i == stateIndex)
					{
						//pair.state = temp;	
						for(list<int>::iterator it = stateIndexes.begin(); it != stateIndexes.end(); it++)
						{
							int ti = *it;

							if(i == ti)
							{
								stringstream ss;
								ss << pair.state << temp;
								pair.state = ss.str();
							}
						}
					}
				}
				

				i++;
			}

			pairs.push_back(pair);

			rows.pop_front();
		}
	}	

	return pairs;
}


//////////////////////////////////////////////////////////////////////////
//Groups pairs into sequential groups with the same size as the tree size
//and converts each group into a branch, each branch in the hash_map is
//unique so no repeated branches
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::PairsToTrees(hash_map<string, PPR> branches, list<PPRObservationAction> pairs, vector<string> observations, int treeLength)
{
	list<string> observationsList;
	copy(observations.begin(), observations.end(), back_inserter(observationsList));
	list<string> empty;

	while(pairs.size() > 0)
	{
		int i = 0;

		ActionNode *rootNode = NULL;
		ActionNode *lastNode = NULL;

		while((i < treeLength) && (pairs.size() > 0))
		{
			PPRObservationAction pair = pairs.front();
			pairs.pop_front();

			if(i == 0)
			{
				rootNode = new ActionNode(pair.action, observationsList, pair.state);
				lastNode = rootNode;
			}
			else if (i < treeLength - 1)
			{
				ActionNode *node = new ActionNode(pair.action, lastNode, observationsList);
				lastNode->AddChildNode(pair.observation, node);
				lastNode = node;
			}
			else
			{
				ActionNode *node = new ActionNode(pair.action, lastNode, empty);
				lastNode->AddChildNode(pair.observation, node);
				lastNode = node;
			}

			i++;
		}	

		//Currently can only merge trees of same length
		//if(rootNode->GetChildTreeLength() == treeLength)
		//{
			PPR branch(rootNode);// = new PPR(rootNode);
			branches = MergeBranchInWithTrees(branches, branch);
		//}		
	}

	return branches;
}

//////////////////////////////////////////////////////////////////////////
//Merges a new PPR in with existing branches using the following steps
//1.If branches.size = 0 then just add and return branches
//2.Else loop through each branch already in branches and check if can
//	be merged
//	2.1. If merging possible with a branch remove the branch from the
//			map and copy both branches
//	2.2. Merge branches
//	2.3. Check that merged branches don t already match one in the hash_map
//			using the ToString function to compare with keys
//	2.4. If no clashes add the newly merged branch to the branches set 
//			and return
//////////////////////////////////////////////////////////////////////////
hash_map<string, PPR> BuilderCSV::MergeBranchInWithTrees(hash_map<string, PPR> branches, PPR branch)
{
	//branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	//return branches;

	bool merged = false;
	string tempPPRKey;
	PPR tempPPR;

	for(hash_map<string, PPR>::iterator it = branches.begin(); (it != branches.end()) && (!merged); it++)
	{
		tempPPR = it->second;
		tempPPRKey = it->first;

		if(tempPPR.CanMergeWithBranch(branch))
		{
			//tempPPR.MergeWithBranch(branch);	
			//merged = true;
			merged = tempPPR.GetRootNode()->MergeWithNode(branch.GetRootNode());
		}
	}

	if(!merged)
	{
		branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	}
	else
	{
		branches.erase(tempPPRKey);
		branches.insert(make_pair(tempPPR.GetRootNode()->ToString(), tempPPR));
	}

	return branches;

	//Step 1
	//if(branches.size() == 0)
	//{
	//	branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	//	return branches;
	//}

	//string eraseKey = "";

	//for(hash_map<string, PPR>::iterator it = branches.begin(); (it != branches.end()) && (eraseKey == ""); it++)
	//{
	//	PPR tempPPR = it->second;
	//	string tempPPRKey = tempPPR.GetRootNode()->ToString();

	//	//If can merge try to merge
	//	if(tempPPR.CanMergeWithBranch(branch))
	//	{
	//		lastMerge last = tempPPR.MergeWithBranch(branch);			
	//		if(branches.find(tempPPR.GetRootNode()->ToString()) == branches.end())
	//		{
	//			eraseKey = tempPPRKey;
	//			//branches.erase(eraseKey);
	//			//branches.insert(make_pair(tempPPR.GetRootNode()->ToString(), tempPPR));
	//		}
	//		else
	//		{
	//			//tempPPR.RevertLastMerge(last);
	//		}
	//	}
	//}

	//if(eraseKey != "")
	//{
	//	hash_map<string, PPR>::iterator it = branches.find(eraseKey);

	//	if(it != branches.end())
	//	{
	//		PPR tempPPR = it->second;
	//		branches.erase(eraseKey);
	//		branches.insert(make_pair(tempPPR.GetRootNode()->ToString(), tempPPR));
	//	}		
	//}
	//else
	//{
	//	//cout << "DONT MERGE" << endl;
	//	////If doesn't already exist then add
	//	if(branches.find(branch.GetRootNode()->ToString()) == branches.end())
	//	{
	//		branches.insert(make_pair(branch.GetRootNode()->ToString(), branch));
	//	}
	//}

	//return branches;
}

//Loops trough each policy tree generating a policy file for each
void BuilderCSV::TreesToPolicyFiles(hash_map<string, PPR> trees, string outputDirectory, string policyPrefix)
{
	mkdir(outputDirectory.c_str());

	int treeNum = 0;

	for(hash_map<string, PPR>::iterator it = trees.begin(); it != trees.end(); it++)
	{
		PPR tree = it->second;

		stringstream ssDirectory;
		ssDirectory << outputDirectory << "\\" << "REP" << treeNum;
		string policyDir = ssDirectory.str();
		mkdir(policyDir.c_str());

		list<ActionNode*> nodes = tree.GetRootNode()->ToListOfNodes();

		for (list<ActionNode*>::iterator itn = nodes.begin(); itn!= nodes.end(); itn++)
		{
			ActionNode* node = *itn;
			int level = node->GetNodeLevel()  + 1;

			string searchString = "";
			string action = node->GetNodeName();	

			if (level != 1)
			{
				ActionNode * currentNode = node;

				while (currentNode->GetParent() != NULL)
				{
					string tempObservation = currentNode->GetParent()->GetObservationForActionNode(currentNode);
					string tempPastAction = currentNode->GetParent()->GetNodeName();
					stringstream ssSearch;

					if (searchString != "") //don't want preceding - for first search string
					{
						ssSearch << searchString << "-" ;
					}			

					ssSearch << tempPastAction << "-" << tempObservation;
					searchString = ssSearch.str();

					currentNode = currentNode->GetParent();
				}
			}			

			if (level == 1)//root node so no need to build a search string
			{
			}
			else//build a search string TODO:
			{
				//Build in reverse iteratively upwards through the tree
			}

			//Write both to file

			stringstream ssFile;
			ssFile << policyDir << "\\" << policyPrefix << level << ".csv";
			string nodeFile = ssFile.str();

			bool exists = false;
			ifstream f(nodeFile.c_str());
			if (f.good()) 
			{
				exists =  true;
			}
			f.close();

			ofstream policyFile;
			policyFile.open (nodeFile.c_str(), ios_base::app);
			if (!exists)
			{
				policyFile << "SearchString,Action," << endl;
			}		

			policyFile << searchString << "," << action << "-" << "," << endl;

			policyFile.close();
		}

		treeNum ++;
	}
}
